// deno-lint-ignore-file no-unused-vars

// Trimming Example: https://tsch.js.org/108

type Trimmed = " " | "\n" | "\t";
type Trim<T extends string> = T extends
  `${Trimmed}${infer Kept}` | `${infer Kept}${Trimmed}` ? Trim<Kept> : T;

const a: Trim<"  str"> = "str";
const b: Trim<"str   "> = "str";
const c: Trim<"  str  "> = "str";
const d: Trim<"  str\n\t   "> = "str";
const e: Trim<" \n str \t"> = "str";

// JSONModel -> Accessing Nested Object
type UUID = ReturnType<typeof crypto.randomUUID>;

type Contact = {
  id: UUID; // UUID as a string
  name: string;
  email: string;
  address: {
    street: string;
    city: string;
    state: string;
    zip: string; 
  },
  phones: {
    type: string;
    number: `${number}-${number}-${number}`; // phone number as a string
  }[];
  tags: string[]; // array of strings
};

// Available paths:
// - "/id"
// - "/name"
// - "/email"
// - "/phones"
// - "/phones/${number}/type"
// - "/phones/${number}/number"
// - "/tags"
// - "/tags/${number}"

// Helper type for string literal number
type StringNumber = `${number}`;

// Helper type to get all possible paths
type PathsOf<T, Prefix extends string = ""> =
  | Prefix
  | (T extends Array<infer Item> ? Item extends object ?
        | `${Prefix}/${number}`
        | PathsOf<Item, `${Prefix}/${StringNumber}`>
    : `${Prefix}/${StringNumber}`
    : T extends object ? {
        [K in keyof T]: K extends string | number
          ? PathsOf<T[K], `${Prefix}/${K}`>
          : never;
      }[keyof T & (string | number)]
    : never);

// Main Path type - remove empty string from the beginning
type Path<T> = Exclude<PathsOf<T>, "">;

// Helper type to get the value at a specific path
type PathValue<T, P extends string> = P extends `/${infer Rest}`
  ? Rest extends keyof T ? T[Rest]
  : Rest extends StringNumber ? T extends Array<infer Item> ? Item
    : never
  : Rest extends `${infer Key}/${infer SubPath}`
    ? Key extends keyof T ? PathValue<T[Key], `/${SubPath}`>
    : Key extends StringNumber
      ? T extends Array<infer Item> ? PathValue<Item, `/${SubPath}`>
      : never
    : never
  : never
  : T;

export interface JSONModel<T> {
  getData(): T;
  setData(data: T): void;
  setProperty<P extends Path<T>>(
    path: P,
    value: PathValue<T, P>,
  ): void;
  getProperty<P extends Path<T>>(
    path: P,
  ): NonNullable<PathValue<T, P>>;
}

const testModel = {} as unknown as JSONModel<Contact>;
const uuid = testModel.getProperty("/id");
const phoneNumber = testModel.getProperty("/phones/0/number"); 
const tag = testModel.getProperty("/tags/0");

testModel.setProperty("/name", "John Doe"); // ok, string expected
testModel.setProperty("/phones/0", {type: 'Work', number:'888-999-0000'}); // ok, string expected
testModel.setProperty("/phones/0/number", "888-999-0000"); // error, not the correct format
